﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using DynaSpace.Entity;

namespace DynaSpace
{
	/// <summary>
	/// Extension metody <see cref="Vector2">vektoru</see>
	/// </summary>
	public static class Vector2Extensions
	{
		/// <summary>
		/// Vrací, zda je předaný vektor v jiném sektoru než aktuální.
		/// </summary>
		/// <param name="vector1">Extension objekt</param>
		/// <param name="vector2">Vektor pro porovnání</param>
		/// <returns>Vrací, zda mají oba vektory shodný sektor</returns>
		public static bool InDiffSector(this Vector2 vector1, Vector2 vector2)
		{
			return (
				((int)(vector1.X / EntityCollection.SECTORSIZE) != (int)(vector2.X / EntityCollection.SECTORSIZE))
				||
				((int)(vector1.Y / EntityCollection.SECTORSIZE) != (int)(vector2.Y / EntityCollection.SECTORSIZE))
				);
		}

		/// <summary>
		/// Vrací, zda je vektor v platném rozsahu předané kolekce.
		/// </summary>
		/// <param name="vector">Extension objekt</param>
		/// <param name="collection">Kolekce proti které se bude kontrolovat umístění vektoru.</param>
		/// <returns>Vrací, zda je vektor mimo kolekci.</returns>
		public static bool IsOutOfRange(this Vector2 vector, EntityCollection collection)
		{
			return (
				vector.X < 0
				|| vector.Y < 0
				|| vector.X >= collection.Width
				|| vector.Y >= collection.Height
				);
		}

		/// <summary>
		/// Vrací, zda je počátek entity (levý horní) v platném rozsahu předané kolekce.
		/// </summary>
		/// <param name="vector">Extension objekt</param>
		/// <param name="collection">Kolekce proti které se bude kontrolovat umístění vektoru.</param>
		/// <param name="size">Velikost entity (implicitně hodnota 1x1)</param>
		/// <returns>Vrací, zda je počátek enity (levý horní) mimo kolekci.</returns>
		public static bool IsOutOfRangeEntity(this Vector2 vector, EntityCollection collection, Vector2 size)
		{
			float widthSpace = (1-size.X) / 2;
			float heightSpace = (1-size.Y) / 2;

			return (
				vector.X + widthSpace < 0
				|| vector.Y + heightSpace < 0
				|| vector.X + 1 -widthSpace  >= collection.Width
				|| vector.Y + 1 - heightSpace >= collection.Height
				);
		}

		/// <summary>
		/// Vrací, zda vektor pozice <see cref="EntityBase">entity</see> spadá do předaného výřezu.
		/// <see cref="EntityBase">Entita</see> se počítá jako objekt s rozměry 1x1 s předaným levým horním rohem jako aktuálním vektorem.
		/// </summary>
		/// <param name="vector">Extension objekt</param>
		/// <param name="left">Levý kraj výřezu</param>
		/// <param name="top">Horní kraj výřezu</param>
		/// <param name="width">Šířka výřezu</param>
		/// <param name="height">Výška výřezu</param>
		/// <returns>Vrací, zda pozice vektoru spadá do výřezu.</returns>
		public static bool IsEntityInRange(this Vector2 vector, float left, float top, float width, float height)
		{
			return (
				vector.X + 1f >= left &&
				vector.Y + 1f >= top &&
				vector.X - .5f < left + width &&
				vector.Y - .5f < top + height
				);
		}

		/// <summary>
		/// Sníží hodnotu vektoru směrem k nulovému vektoru maximální o <paramref name="decreaseVector">snižující vektor</paramref>.
		/// </summary>
		/// <param name="vector">Extension objekt</param>
		/// <param name="decreaseVector">Vektor, který se použije pro snížení</param>
		/// <returns>Vrací snížený vektor</returns>
		public static Vector2 DecreaseVector(this Vector2 vector, Vector2 decreaseVector)
		{
			// pokud je nulový, nepůjde již více ponížit směrem k nule
			if (vector.X == 0 && vector.Y == 0)
				return Vector2.Zero;

			// ponizit vektor smerem k nule
			Vector2 result =
				vector + new Vector2(
					vector.X == 0 ? 0 : (vector.X > 0 ? -decreaseVector.X : decreaseVector.X),
					vector.Y == 0 ? 0 : (vector.Y > 0 ? -decreaseVector.Y : decreaseVector.Y)
					);

			// pokud se ponizi pres nulu, vyrovnat
			result = new Vector2(
				((vector.X < 0 && result.X > 0) || (vector.X > 0 && result.X < 0)) ? 0f : result.X,
				((vector.Y < 0 && result.Y > 0) || (vector.Y > 0 && result.Y < 0)) ? 0f : result.Y
				);

			return result;
		}
	}
}
